#include "protocol.hh"

using namespace DLIB_PROTO;

Protocol::Protocol()
{
    _mode = NONE;
    _initialized = false;
    _connectionConfirmed = false;
    _moduleCode = 0;
    _moduleData = 0;
    _moduleCodeSize = 0;
    _moduleDataSize = 0;
    _moduleCodeToFree = false;
    _moduleDataToFree = false;
}

Protocol::~Protocol()
{
    if (_moduleCode != 0 && _moduleCodeToFree)
        free(_moduleCode);
    if (_moduleData != 0 && _moduleDataToFree)
        free(_moduleData);
}

void Protocol::InitServer(Socket *network)
{
    _mode = SERVER;
    _initialized = true;
    _network = network;
}

void Protocol::InitClient(Socket *network)
{
    _mode = CLIENT;
    _initialized = true;
    _network = network;
}

void Protocol::InitDispatcher(Socket *network)
{
    _mode = DISPATCHER;
    _initialized = true;
    _network = network;
}

bool Protocol::RecvConnectionConfirmation()
{
    int code = ERROR;

    if (_initialized && !_connectionConfirmed && (_mode == CLIENT || _mode == DISPATCHER))
    {
        if (0 < _network->receive(&code) && code == SUCCESS)
            _connectionConfirmed = true;
        else
            cerr << "PROTOCOL: Connection confirmation error!" << endl;
    }

    return (_connectionConfirmed);
}

int Protocol::RecvCode()
{
    int code = ERROR;

    if (_initialized && _connectionConfirmed && (_mode == SERVER || _mode == DISPATCHER))
    {
        if (0 >= _network->receive(&code))
            cerr << "PROTOCOL: Connection receive code error!" << endl;
    }
    else if (_mode == CLIENT)
        cerr << "PROTOCOL: Trying to reveive code from Client connection!" << endl;
    else
        cerr << "PROTOCOL: Trying to receive code from unitialized connection!" << endl;
    return (code);
}

bool Protocol::sendCode(int code)
{
    if (_initialized && _connectionConfirmed)
    {
        if (0 >= _network->send(code))
        {
            cerr << "PROTOCOL: Connection send code error!" << endl;
            return (false);
        }
    }
    return (true);
}

bool Protocol::RecvData()
{
    int code = ERROR;

    switch(_mode)
    {
    case DISPATCHER:
        cerr << "PROTOCOL: Dispatcher trying to retrieve data" <<endl;
        break;
    case SERVER:
        if (!receiveModuleData())
        {
            cerr << "PROTOCOL: Server data reception failed!" << endl;
            return (false);
        }
        break;
    case CLIENT:
        _network->receive(&code);
        if (code == DATA)
        {
            if (!receiveModuleData())
            {
                cerr << "PROTOCOL: Client data reception failed!" << endl;
                return (false);
            }
        }
        else
        {
            cerr << "PROTOCOL: Client trying to retrieve data but server not ready!" << endl;
            return (false);
        }
        break;
    default:
        cerr << "PROTOCOL: Trying to retrieve data before initialization!" << endl;
        return (false);
    }
    return (true);
}

bool Protocol::SendConnectionConfirmation()
{
    if (_initialized && (_mode == SERVER || _mode == DISPATCHER))
    {
        if (0 >= _network->send(SUCCESS))
        {
            cerr << "PROTOCOL: Connection confirmation error!" << endl;
            return (false);
        }
        _connectionConfirmed = true;
    }
    return (true);
}

bool Protocol::SendData(bool executionSuccess)
{
    switch(_mode)
    {
    case DISPATCHER:
        cerr << "PROTOCOL: Dispatcher trying to send data" <<endl;
        break;
    case SERVER:
        _network->send(DATA);
        if (!sendModuleData(executionSuccess))
        {
            cerr << "PROTOCOL: Server data sent failed!" << endl;
            return (false);
        }
        break;
    case CLIENT:
        _network->send(DATA);
        if (_connectionConfirmed)
        {
            if (!sendModuleData(executionSuccess))
            {
                cerr << "PROTOCOL: Client data sent failed!" << endl;
                return (false);
            }
        }
        else
        {
            cerr << "PROTOCOL: Client trying to send data but connection not confirmed!" << endl;
            return (false);
        }
        break;
    default:
        cerr << "PROTOCOL: Trying to send data before initialization!" << endl;
        return (false);
    }
    return (true);
}

bool Protocol::SendServerInfo(string address, int port)
{
    ProtocolDataStruct data;
    ProtocolDataServerInfoStruct *server;

    if (_mode == DISPATCHER)
    {
        memset(&data, 0, sizeof(ProtocolDataStruct));

        // Info paquet
        data._paquetType = EXCHANGE_SERVER_INFO;
        data._paquetSize = PROTOCOL_SERVER_INFO_STRUCT_SIZE;
        server = (ProtocolDataServerInfoStruct*)(data._paquetData);
        server->_serverPort = port;
        memcpy(server->_serverAddress, address.c_str(), ((address.size() < PROTOCOL_NAME_SIZE)? address.size() : PROTOCOL_NAME_SIZE));
        if (0 >= _network->send(&data, sizeof(ProtocolDataStruct)))
            return (false);
    }
    else
    {
        cerr << "PROTOCOL: Non authorized entity trying to send server info!" << endl;
        return (false);
    }
    return (true);
}

bool Protocol::RecvServerInfo(string *address, int *port)
{
    ProtocolDataStruct data;
    ProtocolDataServerInfoStruct *server;

    if (_mode == CLIENT)
    {
        if (0 < _network->receive(&data, sizeof(ProtocolDataStruct)))
        {
            // Info paquet
            if (data._paquetType != EXCHANGE_SERVER_INFO)
                return (false);
            server = (ProtocolDataServerInfoStruct*)(data._paquetData);
            *port = server->_serverPort;
            *address = string(server->_serverAddress);
        }
        else
            return (false);
    }
    else
    {
        cerr << "PROTOCOL: Non authorized entity trying to receive server info!" << endl;
        return (false);
    }
    return (true);
}

void Protocol::GetModuleCode(void **moduleCode, int *moduleCodeSize)
{
    *moduleCode = _moduleCode;
    *moduleCodeSize = _moduleCodeSize;
}

void Protocol::GetModuleData(void **moduleData, int *moduleDataSize)
{
    *moduleData = _moduleData;
    *moduleDataSize = _moduleDataSize;
}

void Protocol::SetModuleCode(void *moduleCode, int moduleCodeSize)
{
    _moduleCode = moduleCode;
    _moduleCodeSize = moduleCodeSize;
}

void Protocol::SetModuleData(void *moduleData, int moduleDataSize)
{
    _moduleData = moduleData;
    _moduleDataSize = moduleDataSize;
}

string Protocol::GetModuleName()
{
    return (_moduleName);
}

void Protocol::SetModuleName(string name)
{
    _moduleName = name;
}

int Protocol::GetId()
{
    if (_initialized)
        return ((int)inet_addr(_network->getSaddr()->getAddress().c_str()) + _network->getSaddr()->getPort());
    return (-1);
}

bool Protocol::receiveModuleData()
{
    ProtocolDataStruct data;
    ProtocolDataInfoStruct *info;
    bool receiveCode;
    int pos;

    if (0 < _network->receive(&data, sizeof(ProtocolDataStruct)))
    {
        // Info paquet
        if (data._paquetType != EXCHANGE_INFO)
            return (false);
        info = (ProtocolDataInfoStruct*)(data._paquetData);
        _moduleCodeSize = info->_moduleSize;
        _moduleDataSize = info->_dataSize;
        _moduleName = string(info->_moduleName);
        if (info->_dataExchanceType == EXECUTION_RESULT)
        {
            // ###########################################################
            // TO MODIFY
            // ###########################################################
            if (info->_executionStatus == ERROR)
            {
                cerr << "PROTOCOL: Server failed to execute module correctly" << endl;
                return (false);
            }
            receiveCode = false;
        }
        else if (info->_dataExchanceType == TO_EXECUTE)
            receiveCode = true;
        else
            return (false);

        // Module code
        if (receiveCode)
        {
            if (0 == (_moduleCode = malloc(_moduleCodeSize)))
            {
                _moduleCodeToFree = true;
                cerr << "PROTOCOL: Malloc error!" << endl;
                return (false);
            }
            pos = 0;
            while (pos < _moduleCodeSize && 0 < _network->receive(&data, sizeof(ProtocolDataStruct)))
            {
                if (data._paquetType != EXCHANGE_MODULE)
                {
                    cerr << "PROTOCOL: Module paquet type error: " << data._paquetType << " instead of " << EXCHANGE_MODULE << endl;
                    return (false);
                }
                memcpy((char*)_moduleCode + pos, data._paquetData, data._paquetSize);
                pos += data._paquetSize;
            }
        }

        // Module data
        if (0 == (_moduleData = malloc(_moduleDataSize)))
        {
            _moduleDataToFree = true;
            cerr << "PROTOCOL: Malloc error!" << endl;
            return (false);
        }
        pos = 0;
        while (pos < _moduleDataSize && 0 < _network->receive(&data, sizeof(ProtocolDataStruct)))
        {
            if (data._paquetType != EXCHANGE_DATA)
            {
                cerr << "PROTOCOL: Data paquet type error!" << endl;
                return (false);
            }
            memcpy((char*)_moduleData + pos, data._paquetData, data._paquetSize);
            pos += data._paquetSize;
        }
    }
    else
        return (false);

    return (true);
}

bool Protocol::sendModuleData(bool success)
{
    ProtocolDataStruct data;
    ProtocolDataInfoStruct *info;
    int pos;
    int paquetSize;

    memset(&data, 0, sizeof(ProtocolDataStruct));

    // Info paquet
    data._paquetType = EXCHANGE_INFO;
    data._paquetSize = PROTOCOL_INFO_STRUCT_SIZE;
    info = (ProtocolDataInfoStruct*)(data._paquetData);
    if (_mode == CLIENT)
        info->_dataExchanceType = TO_EXECUTE;
    else if (_mode == SERVER)
        info->_dataExchanceType = EXECUTION_RESULT;
    else
        info->_dataExchanceType = INVALID;
    if (success)
        info->_executionStatus = SUCCESS;
    else
        info->_executionStatus = ERROR;
    info->_moduleSize = _moduleCodeSize;
    info->_dataSize = _moduleDataSize;
    memcpy(info->_moduleName, _moduleName.c_str(), ((_moduleName.size() < PROTOCOL_NAME_SIZE)? _moduleName.size() : PROTOCOL_NAME_SIZE));
    if (0 >= _network->send(&data, sizeof(ProtocolDataStruct)))
        return (false);

    // Module code
    if (success && _mode == CLIENT)
    {
        pos = 0;
        do
        {
            if ((_moduleCodeSize - pos) >= (int)PROTOCOL_DATA_PAQUET_SIZE)
                paquetSize = PROTOCOL_DATA_PAQUET_SIZE;
            else
                paquetSize = _moduleCodeSize - pos;
            data._paquetType = EXCHANGE_MODULE;
            data._paquetSize = paquetSize;
            memcpy(data._paquetData, (char*)_moduleCode + pos, paquetSize);
            pos += paquetSize;
            if (0 >= _network->send(&data, sizeof(ProtocolDataStruct)))
                return (false);
        } while (pos < _moduleCodeSize);
    }

    // Module data
    if (success && (_mode == CLIENT || _mode == SERVER))
    {
        pos = 0;
        do
        {
            if ((_moduleDataSize - pos) >= (int)PROTOCOL_DATA_PAQUET_SIZE)
                paquetSize = PROTOCOL_DATA_PAQUET_SIZE;
            else
                paquetSize = _moduleDataSize - pos;
            data._paquetType = EXCHANGE_DATA;
            data._paquetSize = paquetSize;
            memcpy(data._paquetData, (char*)_moduleData + pos, paquetSize);
            pos += paquetSize;
            if (0 >= _network->send(&data, sizeof(ProtocolDataStruct)))
                return (false);
        } while (pos < _moduleDataSize);
    }

    return (true);
}
